#include "fs/buf.h"
#include "dev/vio.h"
#include "lib/lock.h"
#include "lib/print.h"
#include "lib/str.h"

#define N_BLOCK_BUF 64
#define BLOCK_NUM_UNUSED 0xFFFFFFFF

// 将buf包装成双向循环链表的node
typedef struct buf_node {
    buf_t buf;
    struct buf_node* next;
    struct buf_node* prev;
} buf_node_t;

// buf cache
static buf_node_t buf_cache[N_BLOCK_BUF];
static buf_node_t head_buf; // ->next 已分配 ->prev 可分配
static spinlock_t lk_buf_cache; // 这个锁负责保护 链式结构 + buf_ref + block_num

// 链表操作
static void insert_head(buf_node_t* buf_node, bool head_next)
{
    // 离开
    if(buf_node->next && buf_node->prev) {
        buf_node->next->prev = buf_node->prev;
        buf_node->prev->next = buf_node->next;
    }

    // 插入
    if(head_next) { // 插入 head->next
        buf_node->prev = &head_buf;
        buf_node->next = head_buf.next;
        head_buf.next->prev = buf_node;
        head_buf.next = buf_node;        
    } else { // 插入 head->prev
        buf_node->next = &head_buf;
        buf_node->prev = head_buf.prev;
        head_buf.prev->next = buf_node;
        head_buf.prev = buf_node;
    }
}

// 初始化
void buf_init()
{
    // 初始化全局自旋锁，用于保护缓冲区
    spinlock_init(&lk_buf_cache, "buf_cache");

    // 初始化链表头节点
    head_buf.next = &head_buf;
    head_buf.prev = &head_buf;

    // 遍历缓冲区数组，初始化每个缓冲区节点
    for (int i = 0; i < N_BLOCK_BUF; i++) {
        // 获取当前缓冲区节点指针
        buf_node_t *bn = &buf_cache[i];

        // 初始化缓冲区块号为未使用状态
        bn->buf.block_num = BLOCK_NUM_UNUSED;

        // 初始化引用计数为 0，表示未被引用
        bn->buf.buf_ref = 0;

        // 初始化缓冲区的睡眠锁
        sleeplock_init(&bn->buf.slk, "buf_sleeplock");

        // 将当前节点插入链表头的前方（空闲列表）
        insert_head(bn, false);
    }
}


/*
    首先假设这个block_num对应的block在内存中有备份, 找到它并上锁返回
    如果找不到, 尝试申请一个无人使用的buf, 去磁盘读取对应block并上锁返回
    如果没有空闲buf, panic报错
    (建议合并xv6的bget())
*/
buf_t* buf_read(uint32 block_num)
{
    buf_node_t *b = NULL;

    // 保护缓存访问
    spinlock_acquire(&lk_buf_cache);

    // 遍历链表，检查是否已在缓存中
    for (b = head_buf.next; b != &head_buf; b = b->next)
    {   
        if (b->buf.block_num == block_num) 
        {
            // 将节点移到链表头以提升访问优先级
            insert_head(b, true);
            b->buf.buf_ref++;

            // 释放链表锁
            spinlock_release(&lk_buf_cache);

            // 获取节点锁
            sleeplock_acquire(&b->buf.slk);
            return &b->buf;
        }
    }

    // 遍历找到一个空闲的缓冲区
    for (b = head_buf.prev; b != &head_buf; b = b->prev)
    {
        if (b->buf.buf_ref == 0)
            break;
    }

    // 如果没有空闲缓冲区，抛出错误
    if (b == &head_buf || b->buf.buf_ref != 0)
        panic("buf_read: no idle buf.");

    // 移动空闲节点到链表头
    insert_head(b, true);

    // 释放链表锁
    spinlock_release(&lk_buf_cache);

    // 获取缓冲区锁
    sleeplock_acquire(&b->buf.slk);

    // 如果缓冲区当前保存了其他数据，将其写回磁盘
    if (b->buf.block_num != BLOCK_NUM_UNUSED)
        buf_write(&b->buf);

    // 更新缓冲区信息
    b->buf.block_num = block_num;
    b->buf.buf_ref = 1;

    // 从磁盘读取对应块的数据
    virtio_disk_rw(&b->buf, 0);

    return &b->buf;
}

// 写函数 (强制磁盘和内存保持一致)
void buf_write(buf_t* buf)
{
    if (!sleeplock_holding(&buf->slk))
        panic("buf_write: write without buf's sleeplock.");

    // 将缓冲区的数据写回磁盘
    virtio_disk_rw(buf, 1);
}

// buf 释放
void buf_release(buf_t* buf)
{
    if (!sleeplock_holding(&buf->slk))
        panic("buf_release: release without buf's sleeplock.");

    // 释放缓冲区的睡眠锁
    sleeplock_release(&buf->slk);

    // 保护链表的访问
    spinlock_acquire(&lk_buf_cache);

    // 减少引用计数
    buf->buf_ref--;

    // 如果引用计数为 0，将节点移到链表末尾
    if (buf->buf_ref == 0)
    {
        buf_node_t *b;
        for (b = head_buf.next; b != &head_buf; b = b->next)
        {
            if (&b->buf == buf)
            {
                insert_head(b, false);
                break;
            }
        }
    }

    // 释放链表锁
    spinlock_release(&lk_buf_cache);
}

// 输出buf_cache的情况
void buf_print()
{
    printf("\nbuf_cache:\n");
    spinlock_acquire(&lk_buf_cache); // 避免竞争条件
    buf_node_t *b;
    for (b = head_buf.next; b != &head_buf; b = b->next)
    {
        printf("buf %d: ref = %d, block_num = %d\n", (int)(b - buf_cache), b->buf.buf_ref, b->buf.block_num);
        printf("data: ");
        for (int i = 0; i < 8; i++)
            printf("%d ", b->buf.data[i]);
        printf("\n");
    }
    spinlock_release(&lk_buf_cache);
}